Logic

At this point we have all of our supplies gathered and we know how they work. So far we know how to make variables and do some basic operations on them. We know a bit about containers and how we can use them to hold variables in various ways. Now how do we use these things to make the computer do our bidding? This notebook is all about logic; making decisions based on conditions(booleans). We will also cover soething called loops which let us do something over and over again with a few lines of code, or, and much more importantly, do something to each element in a container. Let's start with logic.

If and else

We want our computers to make decisions. Computers, however, only know how to ask one type of question: if (something is true) then (do something). To ask this question using code, we use the following format:

if (some condition):
    #put code here

Where (some condition) must give a boolean, or true and false, answer. If the condition is not true, perhaps we want to do something else. To do that we follow this format:

if (some condition):
    #code here happens if the condition is true
else:
    #code here happens if condition is false

It is key to note that the code in the if-block is indented. This tells the computer that the code should only run if the condition is true. Let's take a look at some examples.


In [11]:
if (True):
    print("This will always print")
else:
    print("This will never print")
if (False):
    print("This will never print")
else:
    print("This will always print")


This will always print
This will always print

In [12]:
a = 4
b = 5
if (a<b):
    print("Wow, a was less than b, what a surprise!")
    
if (a>b):
    print("I knew that a was greater than b!")
else:
    print("Wait, a was not greater than b???")

#What does a<b actually return?
print(a<b)
#Aha, it was just a boolean all along


Wow, a was less than b, what a surprise!
Wait, a was not greater than b???
True

In [14]:
la = [1,2,3,4,5,'a','b']
if(1 in la):
    print('1 is in la')
else:
    print('1 is not in la')


1 is in la

Elif

Let's say you want to check a different condition before just saying, "The first condition was false, let's do the else statement." We could just use a second if statement, but instead we have the else-if statement, elif. It allows us to check a second condition after the first one fails. Let us concrete this idea with an example.


In [15]:
#I love food, let's take a look in my fridge
fridge = ['bananas', 'apples', 'water', 'tortillas', 'cheese']
#I want some pizza, but if I don't have any I will settle for a quesadilla which requires tortillas and cheese
if('pizza' in fridge):
    print('Patrick ate pizza and was happy')
elif('tortillas' in fridge and 'cheese' in fridge):
    print('Patrick didn\'t get his pizza, but he did get a quesadilla and is still happy!')
else:
    print('Patrick is still hungry')


Patrick didn't get his pizza, but he did get a quesadilla and is still happy!

Let's revamp that example, but this time, I went out and bought a pizza.


In [16]:
#I love food, let's take a look in my fridge
fridge = ['bananas', 'apples', 'water', 'tortillas', 'cheese', 'pizza']
#I want some pizza, but if I don't have any I will settle for a quesadilla which requires tortillas and cheese
if('pizza' in fridge):
    print('Patrick ate pizza and was happy')
elif('tortillas' in fridge and 'cheese' in fridge):
    print('Patrick didn\'t get his pizza, but he did get a quesadilla and is still happy!')
else:
    print('Patrick is still hungry')


Patrick ate pizza and was happy

Notice that, although I had the fixings for a quesadilla in my fridge, I had pizza so I never needed to check for a tortilla and cheese. This illustrates the fact that elif wont run unless the if statements before it fails. Further, you can stack elif statements forever. Let's see that.


In [17]:
#I love food, let's take a look in my fridge
fridge = ['bananas', 'apples', 'water', 'tortillas', 'beer']
#I want some pizza, but if I don't have any I will settle for a quesadilla which requires tortillas and cheese
if('pizza' in fridge):
    print('Patrick ate pizza and was happy')
elif('tortillas' in fridge and 'cheese' in fridge):
    print('Patrick didn\'t get his pizza, but he did get a quesadilla and is still happy!')
elif('beer' in fridge):
    print('Patrick is still hungry, but he has beer so he is happy')
else:
    print('Patrick is still hungry')


Patrick is still hungry, but he has beer so he is happy

Exercises

  1. Write some "dummy" if, if else, and if elif else statements that will print out exactly what you expect until you feel comfortable with them.
  2. What will be the output of the following code sample:

    if(2<4):

     if(len([1,2,3])<=len(set([1,1,1,2,2,3,3,3]))):
         print("This will certainly print")
     elif(2>1):
         print("Or will this print?")
     else:
         print("It's gotta be this one...")
    

    else:

     print("This won't print...or will it.")

Loops

"I feel like I'm doing this over and over again" -Your computer on loops. Wanna do something 10, 100, n times? Loops are your best friend! Want to loop through a list containing all of your data? Loops are your bestest friend! We will look at two different types of loops, while loops and for loops.

While Loops

While loops will continue to loop until some condition is false. While loops follow the format:

while (some condition):
    #some code here

While loops can go on forever if the condition is never false. This is not the end of the world and you won't crash your computer. To stop a cell that is running, you can click on the stop button in the Jupyter toolbar. Let's see what we can do with this.


In [4]:
t=15
while(t>0):
    print("t-minus " + str(t))
    t-=1


t-minus 15
t-minus 14
t-minus 13
t-minus 12
t-minus 11
t-minus 10
t-minus 9
t-minus 8
t-minus 7
t-minus 6
t-minus 5
t-minus 4
t-minus 3
t-minus 2
t-minus 1

While loops are really good if you want to do something over and over again. Let's generate some fake data with this. I introduce here the range() function. This generates a list of numbers. Let's see briefly how it works.


In [10]:
# Let's make a list of numbers starting at zero and going to 99. range() by default uses a step size of 1
#so this will yield integers from 0 to 99
x = range(0,100)
print(x)
# Unfortunately range does some strange things and doesn't return a list, if you want a list, you already know how to convert it.
print(list(x))


range(0, 100)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

In [16]:
y = []
x = 1
while(x<100):
    y.append(x**5-27*x**2-2300*x**-1+x%(x+1))
    x+=1
print(y)


[-2325.0, -1224.0, -763.6666666666666, 21.0, 1995.0, 6426.666666666667, 15162.42857142857, 30760.5, 56615.444444444445, 97080.0, 157585.9090909091, 244764.33333333334, 366566.07692307694, 532381.7142857143, 753161.6666666666, 1041536.25, 1411935.705882353, 1880710.2222222222, 2466249.947368421, 3189105.0, 4072105.476190476, 5140481.454545454, 6421983.0, 7947000.166666667, 9748683.0, 11863061.538461538, 14329165.814814815, 17189145.85714286, 20488391.689655174, 24275653.333333332, 28603160.80645161, 33526744.125, 39105953.303030305, 45404178.35294118, 52488769.28571428, 60431156.11111111, 69306968.83783785, 79196157.4736842, 90183112.02564102, 102356782.5, 115810798.90243903, 130643591.23809524, 146958509.5116279, 164863943.72727272, 184473443.8888889, 205905840.0, 229285362.06382978, 254741760.08333334, 282410424.06122446, 312432504.0, 344955029.9019608, 380131031.7692308, 418119659.6037736, 459086303.4074074, 503202713.1818182, 550647118.9285715, 601604350.6491228, 656265958.3448275, 714830332.0169492, 777502821.6666666, 844495857.295082, 916029068.9032258, 992329406.4920635, 1073631260.0625, 1160176579.6153846, 1252214995.1515152, 1350003936.6716418, 1453808754.1764705, 1563902837.6666667, 1680567737.142857, 1804093282.6056337, 1934777704.0555556, 2072927751.4931507, 2218858814.918919, 2372895044.3333335, 2535369469.736842, 2706624121.12987, 2887010148.5128207, 3076887941.886076, 3276627251.25, 3486607306.6049385, 3707216937.9512196, 3938854695.2891564, 4181928968.6190476, 4436858107.941176, 4704070543.255814, 4984004904.563218, 5277110141.863636, 5583845645.157304, 5904681364.444445, 6240097929.725275, 6590586771.0, 6956650238.268817, 7338801721.531915, 7737565770.789474, 8153478216.041667, 8587086287.28866, 9038948734.530613, 9509635947.767677]

So that was a cute example of how we can generate some data based on some equation. Later on, however, we will want to graph our data and this requires a second list for our x values. The while loop is cumbersome in the respect and so we now introduce the for loop.

For Loops

A for loop will loop through any container element by element and conveniently place each element in a special new variable. The format of a for loop is as follows:

for (special variable name) in (container we are looping through):
    #do some stuff with, or without, that special variable

The advantage of for loops is that you get each element of some list handed to you on a platter...er, in a variable. Our previous example of generating data now allows us to make a list for our x data and loop through that. Let's see that in action.


In [17]:
x = range(1,100) #Remember that this makes a list of integers from 1 to 99
y = []
for val in x: #val is our special variable here, it will take on the value of every element in x
    print(val)
    y.append(val**2+3*val)
print(y)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
[4, 10, 18, 28, 40, 54, 70, 88, 108, 130, 154, 180, 208, 238, 270, 304, 340, 378, 418, 460, 504, 550, 598, 648, 700, 754, 810, 868, 928, 990, 1054, 1120, 1188, 1258, 1330, 1404, 1480, 1558, 1638, 1720, 1804, 1890, 1978, 2068, 2160, 2254, 2350, 2448, 2548, 2650, 2754, 2860, 2968, 3078, 3190, 3304, 3420, 3538, 3658, 3780, 3904, 4030, 4158, 4288, 4420, 4554, 4690, 4828, 4968, 5110, 5254, 5400, 5548, 5698, 5850, 6004, 6160, 6318, 6478, 6640, 6804, 6970, 7138, 7308, 7480, 7654, 7830, 8008, 8188, 8370, 8554, 8740, 8928, 9118, 9310, 9504, 9700, 9898, 10098]

Again, a neat little example. The true power of for loops comes when we have lists that are not numerical. Let's make every string in a list uppercase.


In [18]:
words = ['i', 'am', 'sorry', 'dave', 'i', 'can\'t', 'do', 'that']
upperwords = []
for word in words: #remember that word will take on the value of every element of words
    print(word)
    upperwords.append(word.upper()) # to make a string uppercase, you can use the .upper() function.
print(upperwords)


i
am
sorry
dave
i
can't
do
that
['I', 'AM', 'SORRY', 'DAVE', 'I', "CAN'T", 'DO', 'THAT']

We have one more special type of loop to cover. List comprehensions; a quick way to make a list in one line.

List Comprehensions

A list comprehension is essentially a for loop sandwiched into a list. The syntax for a list comprehension is as follows:

X = [(expression involving special variable) for (special variable) in (some list)]

For example, we want a list containing x^2 for x in [0,1,2,3,4,5,6,7,8,9,10], we can create this list by using:

Y = [x**2 for x in range(0,11)]

Does this actually work?


In [19]:
y = [x**2 for x in range(0,11)]
print(y)


[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

What about something wierder?


In [22]:
print(words)
wordslength = [len(word) for word in words]
print(wordslength)


['i', 'am', 'sorry', 'dave', 'i', "can't", 'do', 'that']
[1, 2, 5, 4, 1, 5, 2, 4]

My god, it worked! Think of the possibilities! With these new tools we can do 90% of all programming we will ever do. Pretty neat huh. I would like to show you one more example of list comprehensions.


In [24]:
# I only want words with length less than 3
newwords = [word for word in words if len(word)<3]
print(newwords)


['i', 'am', 'i', 'do']

This is an incredibly powerful tool that you will use on ocassion, so keep it in your noggin somewhere.

Exercises

  1. Write a while loop that counts to 25 and on every number that is divisible by 3 prints out "Flarbos".
  2. Do the same thing as in 1. but with a for loop.
  3. Do Project Euler problem #2 https://projecteuler.net/problem=2
  4. Do Project Euler problem #25 https://projecteuler.net/problem=25
  5. Find the smallest integer k such that k, k/13, and k^2/43 are divisible by 244 using a for loop.
  6. Repeat 5. using a list comprehension.

In [ ]:


In [ ]:


In [ ]: